package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.container.Context;
import server.container.Host;
import server.container.Mapper;
import server.servlet.ContextLoader;
import server.thread.RequestProcessor;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.*;

/**
 * @description:
 * @author: Archie Wan
 * @time: 2020/12/27 13:39
 */
public class BootStrap {
    
    /* 用于存储 web.xml 中servlet的映射关系 */
    /*private Map<String,HttpServlet> servletMap = new HashMap<String,HttpServlet>();*/
    
    /* 用于存储 server.xml 中的 Host 映射关系 */
    private Mapper mapper = new Mapper();
    
    /* 定义socket监听的端口号 */
    private int port = 8080;
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    
    /**
     * MiniCat启动需要初始化展开的一些操作
     */
    public void start() {
    
        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize =50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
    
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
    
        // 加载解析相关的配置，web.xml
        loadServerXml();
        
        /*
         * 完成 MiniCat 5.0版本
         * 需求：多线程请求
         */
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("===> MiniCat start on port：" + port);
    
            System.out.println("=========>>>>>>使用线程池进行多线程改造");
            // 多线程改造 (且使用线程池节约资源)
            while(true) {
                Socket socket = serverSocket.accept();
                RequestProcessor requestProcessor = new RequestProcessor(socket,null, mapper);
                threadPoolExecutor.execute(requestProcessor);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    }
    
    /**
     * 加载解析 server.xml，初始化不同 webapp 中的 Servlet
     */
    private void loadServerXml() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            
            // 读取 server.xml 中的 <Connector> 节点并获取到用户配置的 指定port
            Element connector = (Element) rootElement.selectSingleNode("//Connector");
            String port = connector.attributeValue("port");
            setPort(Integer.parseInt(port)); // 覆盖当前默认port
    
            // 读取 server.xml 中的 <Host> 节点们 并遍历获取其中需要的信息
            List<Element> hosts = rootElement.selectNodes("//Host");
            for (Element everyHost : hosts) {
                /* 将 host 信息封装 */
                String name = everyHost.attributeValue("name");
                String appBase = everyHost.attributeValue("appBase");
                Host host = new Host(name);
                /* 读取每个<Host> 中的 <Context>信息 */
                List<Element> contexts = everyHost.selectNodes("Context");
                Context context = null;
                if (contexts != null) {
                    for (Element everyContext : contexts) {
                        String contextName = everyContext.attributeValue("name");
                        String path = everyContext.attributeValue("path");
                        /* 将 context 信息封装 */
                        context = new Context(contextName, appBase + "/" + path);
                        innerLoad(context, host);
                    }
                } else {
                    context = new Context("/", appBase); // 没有配置 <Context> 时
                    innerLoad(context, host);
                }
                // ----- 内存写入host集合映射 -------
                mapper.addHost(host);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    private void innerLoad(Context context, Host host) {
        // 1. ----- 通过 context 的信息 创建 Loader 实体  -------
        //    ----- (原本对于 web.xml 中 Servlet 映射的解析放在load()里 ) -------
        ContextLoader contextLoader = new ContextLoader(context);
        contextLoader.load();
        // 2. ----- context 信息 传入 host 中 -------
        host.addContext(context);
    }
    
    public static void main(String[] args) {
        BootStrap bootStrapV5 = new BootStrap();
        bootStrapV5.start();
    }
    
}
